home *** CD-ROM | disk | FTP | other *** search
- //**************************************************************************
- //* Curve Applet by Michael Heinrichs
- //*
- //* This applet allows the user to specify control points to be used for
- //* plotting curves. Three curve types are supported: Hermite, Bezier,
- //* and B-Spline. The user can add, delete and move control points.
- //*
- //* This applet was inspired by the DrawTest demo applet included in the
- //* 1.0 beta Java Developer's Kit from Sun Microsystems.
- //* If you are interested in the algorithms used to display the curves,
- //* Please see the book: "Computer Graphics. Principles and Practice" by
- //* Foley, VanDam, Feiner, and Hughes.
- //**************************************************************************
-
- import java.awt.*;
- import java.applet.*;
-
- import java.util.Vector;
-
- public class Curve extends Applet {
- public void init() {
- setLayout(new BorderLayout());
- CurvePanel dp = new CurvePanel();
- add("Center", dp);
- add("South",new CurveControls(dp));
- add("North",new CurveControls2(dp));
- }
-
- public boolean handleEvent(Event e) {
- switch (e.id) {
- case Event.WINDOW_DESTROY:
- System.exit(0);
- return true;
- default:
- return false;
- }
- }
-
- public static void main(String args[]) {
- Frame f = new Frame("Curve");
- Curve curve = new Curve();
- curve.init();
- curve.start();
-
- f.add("Center", curve);
- f.show();
- }
- }
-
- class ControlPoint extends Object {
- public int x;
- public int y;
- public static final int PT_SIZE = 4;
-
- public ControlPoint(int a, int b) {
- x = a;
- y = b;
- }
-
- public boolean within(int a, int b) {
- if (a >= x - PT_SIZE &&
- b >= y - PT_SIZE &&
- a <= x + PT_SIZE &&
- b <= y + PT_SIZE)
- return true;
- else
- return false;
- }
- }
-
- class CurvePanel extends Panel {
- public static final int HERMITE = 0;
- public static final int BEZIER = 1;
- public static final int BSPLINE = 2;
- private int mode = HERMITE;
-
- public static final int ADD = 0;
- public static final int MOVE = 1;
- public static final int DELETE = 2;
- private int action = ADD;
-
- private Vector points = new Vector(16,4);
-
- // If a control point is being moved, this is the index into the list
- // of the moving point. Otherwise it contains -1
- private int moving_point;
- private int precision;
- private static float hermiteMatrix[][] = new float[4][4];
- private static float bezierMatrix[][] = new float[4][4];
- private static float bsplineMatrix[][] = new float[4][4];
- private float eMatrix[][] = new float[4][4];
-
- // Initialize the curve-type matrices
- static {
- hermiteMatrix[0][0] = 2;
- hermiteMatrix[0][1] = -2;
- hermiteMatrix[0][2] = 1;
- hermiteMatrix[0][3] = 1;
- hermiteMatrix[1][0] = -3;
- hermiteMatrix[1][1] = 3;
- hermiteMatrix[1][2] = -2;
- hermiteMatrix[1][3] = -1;
- hermiteMatrix[2][0] = 0;
- hermiteMatrix[2][1] = 0;
- hermiteMatrix[2][2] = 1;
- hermiteMatrix[2][3] = 0;
- hermiteMatrix[3][0] = 1;
- hermiteMatrix[3][1] = 0;
- hermiteMatrix[3][2] = 0;
- hermiteMatrix[3][3] = 0;
-
- bezierMatrix[0][0] = -1;
- bezierMatrix[0][1] = 3;
- bezierMatrix[0][2] = -3;
- bezierMatrix[0][3] = 1;
- bezierMatrix[1][0] = 3;
- bezierMatrix[1][1] = -6;
- bezierMatrix[1][2] = 3;
- bezierMatrix[1][3] = 0;
- bezierMatrix[2][0] = -3;
- bezierMatrix[2][1] = 3;
- bezierMatrix[2][2] = 0;
- bezierMatrix[2][3] = 0;
- bezierMatrix[3][0] = 1;
- bezierMatrix[3][1] = 0;
- bezierMatrix[3][2] = 0;
- bezierMatrix[3][3] = 0;
-
- float mult = (float)(1.0/6.0);
- bsplineMatrix[0][0] = -mult;
- bsplineMatrix[0][1] = 3 * mult;
- bsplineMatrix[0][2] = -3 * mult;
- bsplineMatrix[0][3] = mult;
- bsplineMatrix[1][0] = 3 * mult;
- bsplineMatrix[1][1] = -6 * mult;
- bsplineMatrix[1][2] = 3 * mult;
- bsplineMatrix[1][3] = 0;
- bsplineMatrix[2][0] = -3 * mult;
- bsplineMatrix[2][1] = 0;
- bsplineMatrix[2][2] = 3 * mult;
- bsplineMatrix[2][3] = 0;
- bsplineMatrix[3][0] = mult;
- bsplineMatrix[3][1] = 4 * mult;
- bsplineMatrix[3][2] = mult;
- bsplineMatrix[3][3] = 0;
- }
-
- public CurvePanel() {
- setBackground(Color.white);
- }
-
- private void calcEMatrix(int prec) {
- // In order to use the "forward difference" method of curve plotting,
- // we must generate this matrix. The parameter indicates the precision;
- // the number of line segments to use for each curve.
-
- float step = (float) (1.0/(float)prec);
-
- eMatrix[0][0] = 0;
- eMatrix[0][1] = 0;
- eMatrix[0][2] = 0;
- eMatrix[0][3] = 1;
-
- eMatrix[1][2] = step;
- eMatrix[1][1] = eMatrix[1][2] * step;
- eMatrix[1][0] = eMatrix[1][1] * step;
- eMatrix[1][3] = 0;
-
- eMatrix[2][0] = 6 * eMatrix[1][0];
- eMatrix[2][1] = 2 * eMatrix[1][1];
- eMatrix[2][2] = 0;
- eMatrix[2][3] = 0;
-
- eMatrix[3][0] = eMatrix[2][0];
- eMatrix[3][1] = 0;
- eMatrix[3][2] = 0;
- eMatrix[3][3] = 0;
- }
-
- public void setAction(int action) {
- // Change the action type
- switch (action) {
- case ADD:
- case MOVE:
- case DELETE:
- this.action = action;
- break;
- default:
- throw new IllegalArgumentException();
- }
- }
-
- public void setCurveType(int mode) {
- // Change the curve display type
- switch (mode) {
- case HERMITE:
- case BEZIER:
- case BSPLINE:
- this.mode = mode;
- break;
- default:
- throw new IllegalArgumentException();
- }
- }
-
- public void setPrecision(int prec) {
- precision = prec;
- calcEMatrix(prec);
- }
-
- public void clearPoints() {
- points.removeAllElements();
- }
-
- private int findPoint(int a, int b) {
- // Scan the list of control points to find out which (if any) point
- // contains the coordinates: a,b.
- // If a point is found, return the point's index, otherwise return -1
- int max = points.size();
-
- for(int i = 0; i < max; i++) {
- ControlPoint pnt = (ControlPoint)points.elementAt(i);
- if (pnt.within(a,b)) {
- return i;
- }
- }
- return -1;
- }
-
- public boolean handleEvent(Event e) {
- switch (e.id) {
- case Event.MOUSE_DOWN:
- // How we handle a MOUSE_DOWN depends on the action mode
- switch (action) {
- case ADD:
- // Add a new control point at the specified location
- ControlPoint pnt;
- points.addElement(pnt = new ControlPoint(e.x, e.y));
- repaint();
- break;
- case MOVE:
- // Attempt to select the point at the location specified.
- // If there is no point at the location, findPoint returns
- // -1 (i.e. there is no point to be moved)
- moving_point = findPoint(e.x, e.y);
- break;
- case DELETE:
- // Delete a point if one has been clicked
- int delete_pt = findPoint(e.x, e.y);
- if(delete_pt >= 0) {
- points.removeElementAt(delete_pt);
- repaint();
- }
- break;
- default:
- throw new IllegalArgumentException();
- }
- return true;
- case Event.MOUSE_UP:
- // We only care about MOUSE_UP's if we've been moving a control
- // point. If so, drop the control point.
- if (moving_point >=0) {
- moving_point = -1;
- repaint();
- }
- return true;
- case Event.MOUSE_DRAG:
- // We only care about MOUSE_DRAG's while we are moving a control
- // point. Otherwise, do nothing.
- if (moving_point >=0) {
- ControlPoint pnt = (ControlPoint) points.elementAt(moving_point);
- pnt.x = e.x;
- pnt.y = e.y;
- repaint();
- }
- return true;
- case Event.WINDOW_DESTROY:
- System.exit(0);
- return true;
- default:
- return false;
- }
- }
-
- private void multMatrix(float m[][], float g[][], float mg[][]) {
- // This function performs the meat of the calculations for the
- // curve plotting. Note that it is not a matrix multiplier in the
- // pure sense. The first matrix is the curve matrix (each curve type
- // has its own matrix), and the second matrix is the geometry matrix
- // (defined by the control points). The result is returned in the
- // third matrix.
-
- // First clear the return array
- for(int i=0; i<4; i++)
- for(int j=0; j<2; j++)
- mg[i][j]=0;
-
- // Perform the matrix math
- for(int i=0; i<4; i++)
- for(int j=0; j<2; j++)
- for(int k=0; k<4; k++)
- mg[i][j]=mg[i][j] + (m[i][k] * g[k][j]);
- }
-
- public void paint(Graphics g) {
- int np = points.size(); // number of points
- float geom[][] = new float[4][2]; // geometry matrix
- float mg[][] = new float[4][2]; //
- float plot[][] = new float[4][2];
-
- g.setColor(getForeground());
- g.setPaintMode();
-
- // draw a border around the canvas
- g.drawRect(0,0, size().width-1, size().height-1);
-
- // draw the control points
- for (int i=0; i < np; i++) {
- ControlPoint p = (ControlPoint)points.elementAt(i);
- g.drawRect(p.x-p.PT_SIZE, p.y-p.PT_SIZE, p.PT_SIZE*2, p.PT_SIZE*2);
- g.drawString(String.valueOf(i),p.x+p.PT_SIZE,p.y-p.PT_SIZE);
- }
-
- for(int i = 0; i < np-3;) {
- // Four control points are needed to create a curve.
- // If all the control points are used, the last series of four
- // points begins with point np-4.
- switch (mode) {
- // The geometry matrix for a series of control points is
- // different for each curve type.
- case(HERMITE):
- geom[0][0] = ((ControlPoint)points.elementAt(i)).x;
- geom[0][1] = ((ControlPoint)points.elementAt(i)).y;
- geom[1][0] = ((ControlPoint)points.elementAt(i+3)).x;
- geom[1][1] = ((ControlPoint)points.elementAt(i+3)).y;
- geom[2][0] = ((ControlPoint)points.elementAt(i+1)).x-geom[0][0];
- geom[2][1] = ((ControlPoint)points.elementAt(i+1)).y-geom[0][1];
- geom[3][0] = geom[1][0]-((ControlPoint)points.elementAt(i+2)).x;
- geom[3][1] = geom[1][1]-((ControlPoint)points.elementAt(i+2)).y;
- multMatrix(hermiteMatrix, geom, mg);
-
- // The beginning of the next Hermite curve is the last
- // point of the previous curve.
- i += 3;
- break;
- case(BEZIER):
- for(int j = 0; j <4 ;j++) {
- geom[j][0] = ((ControlPoint)points.elementAt(i+j)).x;
- geom[j][1] = ((ControlPoint)points.elementAt(i+j)).y;
- }
- multMatrix(bezierMatrix, geom, mg);
-
- // The beginning of the next Bezier curve is the last
- // point of the previous curve.
- i += 3;
- break;
- case(BSPLINE):
- for(int j = 3; j >= 0; j--) {
- geom[3-j][0] = ((ControlPoint)points.elementAt(i+j)).x;
- geom[3-j][1] = ((ControlPoint)points.elementAt(i+j)).y;
- }
- multMatrix(bsplineMatrix, geom, mg);
-
- // B-Spline is the slowest curve, since the beginning of
- // the next series of four control points is the second
- // control point of the previous series.
- i++;
- break;
- }
-
- // In order to plot the curve using forward differences
- // (a speedier way to plot the curve), another matrix
- // calculation is required, taking into account the precision
- // of the curve.
- multMatrix(eMatrix, mg, plot);
- float startX = plot[0][0];
- float x = startX;
- float startY = plot[0][1];
- float y = startY;
- // Plot the curve using the forward difference method
- for(int j=0; j<precision; j++) {
- x += plot[1][0];
- plot[1][0] += plot[2][0];
- plot[2][0] += plot[3][0];
- y += plot[1][1];
- plot[1][1] += plot[2][1];
- plot[2][1] += plot[3][1];
- g.drawLine((int)startX,(int)startY,(int)x,(int)y);
- startX = x;
- startY = y;
- }
- }
- }
- }
-
- class CurveControls extends Panel {
- CurvePanel target;
- Checkbox cb_add;
- Checkbox cb_move;
- Checkbox cb_delete;
- String st_add_label = "Add Points";
- String st_move_label = "Move Points";
- String st_delete_label = "Delete Points";
-
- public CurveControls(CurvePanel target) {
- this.target = target;
- setLayout(new FlowLayout(FlowLayout.CENTER));
- setBackground(Color.lightGray);
- Button clear = new Button("Clear");
- add("West",clear);
-
- CheckboxGroup action_group = new CheckboxGroup();
- add(cb_add = new Checkbox(st_add_label, action_group, true));
- add(cb_move = new Checkbox(st_move_label, action_group, false));
- add(cb_delete = new Checkbox(st_delete_label, action_group, false));
- }
-
- public void paint(Graphics g) {
- Rectangle r = bounds();
-
- g.setColor(Color.lightGray);
- g.draw3DRect(0, 0, r.width, r.height, false);
- }
-
- public boolean action(Event e, Object arg) {
- if (e.target instanceof Checkbox) {
- String cbox = ((Checkbox)(e.target)).getLabel();
- if (cbox.equals(st_add_label)) {
- target.setAction(CurvePanel.ADD);
- } else if (cbox.equals(st_move_label)) {
- target.setAction(CurvePanel.MOVE);
- } else if (cbox.equals(st_delete_label)) {
- target.setAction(CurvePanel.DELETE);
- }
- } else if (e.target instanceof Button) {
- String button = ((Button)(e.target)).getLabel();
- if (button.equals("Clear")) {
- target.clearPoints();
-
- // After clearing the control points, put the user back into
- // ADD mode, since none of the other modes make any sense.
- cb_add.setState(true);
- cb_delete.setState(false);
- cb_move.setState(false);
- target.setAction(CurvePanel.ADD);
- target.repaint();
- }
- }
- return true;
- }
- }
-
- class CurveControls2 extends Panel {
- CurvePanel target;
- Checkbox cb_hermite;
- Checkbox cb_bezier;
- Checkbox cb_bspline;
- String st_hermite_label = "Hermite";
- String st_bezier_label = "Bezier";
- String st_bspline_label = "B-Spline";
- Scrollbar scroll;
- Label precision_display;
-
- public CurveControls2(CurvePanel target) {
- this.target = target;
- setLayout(new FlowLayout(1));
-
- CheckboxGroup type_group = new CheckboxGroup();
- add(cb_hermite = new Checkbox(st_hermite_label, type_group, true));
- add(cb_bezier = new Checkbox(st_bezier_label, type_group, false));
- add(cb_bspline = new Checkbox(st_bspline_label, type_group, false));
-
- // Set up the scrollbar: Value 15, Page 5, Min 2, Max 40
- add(scroll = new Scrollbar(Scrollbar.HORIZONTAL, 15, 5, 2, 40));
- target.setPrecision(scroll.getValue());
- add(precision_display = new Label("Precision: "+
- String.valueOf(scroll.getValue()),Label.LEFT));
- }
-
- public void paint(Graphics g) {
- Rectangle r = bounds();
-
- g.setColor(Color.lightGray);
- g.draw3DRect(0, 0, r.width, r.height, false);
- }
-
- public boolean handleEvent(Event e) {
- switch (e.id) {
- case Event.SCROLL_LINE_DOWN:
- case Event.SCROLL_LINE_UP:
- case Event.SCROLL_PAGE_DOWN:
- case Event.SCROLL_PAGE_UP:
- case Event.SCROLL_ABSOLUTE:
- // For any of these events, get the precision value from the
- // scrollbar, and update the curve precision, and the label.
- target.setPrecision(((Scrollbar)e.target).getValue());
- precision_display.setText("Precision: "+
- String.valueOf(((Scrollbar)e.target).getValue()));
- target.repaint();
- return true;
- case Event.ACTION_EVENT:
- // Handle other action events
- if (e.target instanceof Checkbox) {
- String cbox = ((Checkbox)(e.target)).getLabel();
- if (cbox.equals(st_hermite_label)) {
- target.setCurveType(CurvePanel.HERMITE);
- } else if (cbox.equals(st_bezier_label)) {
- target.setCurveType(CurvePanel.BEZIER);
- } else if (cbox.equals(st_bspline_label)) {
- target.setCurveType(CurvePanel.BSPLINE);
- }
- }
- target.repaint();
- return(true);
- default:
- return(false);
- }
- }
- }
-